[id].vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <template>
  2. <div class="admin--branch-form">
  3. <div v-if="isLoading" class="admin--loading">
  4. 데이터를 불러오는 중...
  5. </div>
  6. <form v-else @submit.prevent="handleSubmit" class="admin--form">
  7. <!-- 지점명 -->
  8. <div class="admin--form-group">
  9. <label class="admin--form-label">지점명 <span class="admin--required">*</span></label>
  10. <input
  11. v-model="formData.name"
  12. type="text"
  13. class="admin--form-input"
  14. placeholder="지점명을 입력하세요"
  15. required
  16. >
  17. </div>
  18. <!-- 대표번호 -->
  19. <div class="admin--form-group">
  20. <label class="admin--form-label">대표번호 <span class="admin--required">*</span></label>
  21. <input
  22. v-model="formData.phone"
  23. type="tel"
  24. class="admin--form-input"
  25. placeholder="02-1234-5678"
  26. required
  27. >
  28. </div>
  29. <!-- 주소 -->
  30. <div class="admin--form-group">
  31. <label class="admin--form-label">주소 <span class="admin--required">*</span></label>
  32. <input
  33. v-model="formData.address"
  34. type="text"
  35. class="admin--form-input"
  36. placeholder="주소를 입력하세요"
  37. required
  38. >
  39. </div>
  40. <!-- 상세주소 -->
  41. <div class="admin--form-group">
  42. <label class="admin--form-label">상세주소</label>
  43. <input
  44. v-model="formData.detail_address"
  45. type="text"
  46. class="admin--form-input"
  47. placeholder="상세주소를 입력하세요"
  48. >
  49. </div>
  50. <!-- 위도/경도 -->
  51. <div class="admin--form-group">
  52. <label class="admin--form-label">위치 좌표</label>
  53. <div class="admin--coordinate-group">
  54. <div class="admin--coordinate-item">
  55. <label>위도</label>
  56. <input
  57. v-model.number="formData.latitude"
  58. type="number"
  59. step="any"
  60. class="admin--form-input"
  61. placeholder="37.5665"
  62. >
  63. </div>
  64. <div class="admin--coordinate-item">
  65. <label>경도</label>
  66. <input
  67. v-model.number="formData.longitude"
  68. type="number"
  69. step="any"
  70. class="admin--form-input"
  71. placeholder="126.9780"
  72. >
  73. </div>
  74. </div>
  75. </div>
  76. <!-- 영업시간 -->
  77. <div class="admin--form-group">
  78. <label class="admin--form-label">영업시간</label>
  79. <textarea
  80. v-model="formData.business_hours"
  81. class="admin--form-textarea"
  82. rows="3"
  83. placeholder="평일: 09:00 - 18:00&#10;주말: 10:00 - 17:00"
  84. ></textarea>
  85. </div>
  86. <!-- 버튼 영역 -->
  87. <div class="admin--form-actions">
  88. <button
  89. type="submit"
  90. class="admin--btn admin--btn-primary"
  91. :disabled="isSaving"
  92. >
  93. {{ isSaving ? '저장 중...' : '확인' }}
  94. </button>
  95. <button
  96. type="button"
  97. class="admin--btn admin--btn-secondary"
  98. @click="goToList"
  99. >
  100. 목록
  101. </button>
  102. </div>
  103. <!-- 성공/에러 메시지 -->
  104. <div v-if="successMessage" class="admin--alert admin--alert-success">
  105. {{ successMessage }}
  106. </div>
  107. <div v-if="errorMessage" class="admin--alert admin--alert-error">
  108. {{ errorMessage }}
  109. </div>
  110. </form>
  111. </div>
  112. </template>
  113. <script setup>
  114. import { ref, onMounted } from 'vue'
  115. import { useRoute, useRouter } from 'vue-router'
  116. definePageMeta({
  117. layout: 'admin',
  118. middleware: ['auth']
  119. })
  120. const route = useRoute()
  121. const router = useRouter()
  122. const { get, put } = useApi()
  123. const isLoading = ref(true)
  124. const isSaving = ref(false)
  125. const successMessage = ref('')
  126. const errorMessage = ref('')
  127. const formData = ref({
  128. name: '',
  129. phone: '',
  130. address: '',
  131. detail_address: '',
  132. latitude: null,
  133. longitude: null,
  134. business_hours: ''
  135. })
  136. // 데이터 로드
  137. const loadBranch = async () => {
  138. isLoading.value = true
  139. const id = route.params.id
  140. const { data, error } = await get(`/branch/${id}`)
  141. if (data?.success && data?.data) {
  142. const branch = data.data
  143. formData.value = {
  144. name: branch.name || '',
  145. phone: branch.phone || '',
  146. address: branch.address || '',
  147. detail_address: branch.detail_address || '',
  148. latitude: branch.latitude || null,
  149. longitude: branch.longitude || null,
  150. business_hours: branch.business_hours || ''
  151. }
  152. }
  153. isLoading.value = false
  154. }
  155. // 폼 제출
  156. const handleSubmit = async () => {
  157. successMessage.value = ''
  158. errorMessage.value = ''
  159. // 유효성 검사
  160. if (!formData.value.name) {
  161. errorMessage.value = '지점명을 입력하세요.'
  162. return
  163. }
  164. if (!formData.value.phone) {
  165. errorMessage.value = '대표번호를 입력하세요.'
  166. return
  167. }
  168. if (!formData.value.address) {
  169. errorMessage.value = '주소를 입력하세요.'
  170. return
  171. }
  172. isSaving.value = true
  173. try {
  174. const id = route.params.id
  175. const { data, error } = await put(`/branch/${id}`, formData.value)
  176. if (error || !data?.success) {
  177. errorMessage.value = error?.message || data?.message || '수정에 실패했습니다.'
  178. } else {
  179. successMessage.value = data.message || '지점이 수정되었습니다.'
  180. setTimeout(() => {
  181. router.push('/admin/branch/list')
  182. }, 1000)
  183. }
  184. } catch (error) {
  185. errorMessage.value = '서버 오류가 발생했습니다.'
  186. console.error('Save error:', error)
  187. } finally {
  188. isSaving.value = false
  189. }
  190. }
  191. // 목록으로 이동
  192. const goToList = () => {
  193. router.push('/admin/branch/list')
  194. }
  195. onMounted(() => {
  196. loadBranch()
  197. })
  198. </script>
  199. <style scoped>
  200. .admin--coordinate-group {
  201. display: flex;
  202. gap: 16px;
  203. }
  204. .admin--coordinate-item {
  205. flex: 1;
  206. }
  207. .admin--coordinate-item label {
  208. display: block;
  209. margin-bottom: 8px;
  210. font-size: 14px;
  211. color: #666;
  212. }
  213. </style>